#include "coin/proto/coin_telemetry.pb.h"
#include "coin/proto/coin_request.pb.h"
#include "coin2/exchange/base/feed/update.h"
#include "coin2/exchange/base/log/health_reporter.h"

namespace coin2::strategy::util {

namespace {

void AddComponentLatencyStats(
    coin::proto::StrategyLatencyStatsProto& latency_stats,
    coin::proto::StrategyComponentLatencyStatsProto::LatencyType latency_type,
    const coin::proto::AccountRequestProto& acc_req,
    const std::string& symbol,
    double latency_value) {
  auto new_stats = latency_stats.add_each_stats();
  new_stats->set_latency_type(latency_type);
  *new_stats->mutable_account_request() = acc_req;
  new_stats->set_symbol(symbol);
  new_stats->set_latency_value(latency_value);
}

}  // namespace

void WriteFeedHealthReport(
    const coin2::exchange::base::feed::FeedUpdate& upd,
    const coin::proto::StrategyRequestProto& strat_req,
    const coin::proto::AccountRequestProto& acc_req,
    const std::string symbol,
    exchange::base::strategy_util::PrometheusHealthReporter* health_reporter) {
  using LatencyProto = coin::proto::StrategyComponentLatencyStatsProto;
  if (!health_reporter) {
    LOG(ERROR) << "doesn't have health report writer";
    return;
  }
  coin::proto::StrategyLatencyStatsProto latency_stats;
  CHECK(strat_req.has_strategy_name());
  latency_stats.mutable_strategy()->set_strategy_name(strat_req.strategy_name());
  auto raw_feed_exchange_published_ts = upd.ts().exchange_publish_timestamp();
  auto raw_feed_received_ts = upd.ts().raw_rx_timestamp();
  auto raw_feed_parse_started_ts = upd.ts().main_rx_timestamp();
  auto on_feed_ts = upd.ts().main_tx_timestamp();

  auto latency_type = LatencyProto::ET_FT_DIFF;
  if (upd.is_book()) {
    latency_type = LatencyProto::BOOK_ET_FT_DIFF;
  } else if (upd.is_trade()) {
    latency_type = LatencyProto::TRADE_ET_FT_DIFF;
  }
  auto latency_value = raw_feed_received_ts - raw_feed_exchange_published_ts;
  AddComponentLatencyStats(latency_stats, latency_type, acc_req, symbol, latency_value);

  latency_type = LatencyProto::FEED_PENDING;
  latency_value = raw_feed_parse_started_ts - raw_feed_received_ts;
  AddComponentLatencyStats(latency_stats, latency_type, acc_req, symbol, latency_value);

  latency_value = on_feed_ts - raw_feed_parse_started_ts;
  if (upd.is_book()) {
    latency_type = LatencyProto::BOOK_FEED_PARSER;
    AddComponentLatencyStats(
        latency_stats, latency_type, acc_req, symbol, latency_value);
  } else if (upd.is_trade()) {
    latency_type = LatencyProto::TRADE_FEED_PARSER;
    AddComponentLatencyStats(
        latency_stats, latency_type, acc_req, symbol, latency_value);
  }
  health_reporter->Write(latency_stats);
}

}  // namespace coin2::strategy::util
